#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <pthread.h>
//编写基于BlockingQueue的⽣产者消费者模型（多生产，多消费）,阻塞队列不用改变
int main()
{
    BlockQueue<task_t> *bq = new BlockQueue<task_t>();

    // 构建生产和消费者
    pthread_t c[2], p[3];

    pthread_create(c, nullptr, consumer, bq);
    pthread_create(c+1, nullptr, consumer, bq);
    pthread_create(p, nullptr, productor, bq);
    pthread_create(p+1, nullptr, productor, bq);
    pthread_create(p+2, nullptr, productor, bq);

    pthread_join(c[0], nullptr);
    pthread_join(c[1], nullptr);
    pthread_join(p[0], nullptr);
    pthread_join(p[1], nullptr);
    pthread_join(p[2], nullptr);

    return 0;
}





//编写基于BlockingQueue的⽣产者消费者模型（单生产，单消费）
//BlockQueue.hpp
// 阻塞队列的实现
#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

const int defaultcap = 5; // for test

template <typename T>
class BlockQueue
{
private:
    bool IsFull() { return _q.size() >= _cap; }
    bool IsEmpty() { return _q.empty(); }

public:
    BlockQueue(int cap = defaultcap)
        : _cap(cap), _csleep_num(0), _psleep_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_cond_init(&_empty_cond, nullptr);
    }
    void Equeue(const T &in)
    {
        pthread_mutex_lock(&_mutex);
        // 生产者调用
        while (IsFull()) //while增加代码的鲁棒性，防止被伪唤醒
        {
            // 应该让生产者线程进行等待
            // 重点1：pthread_cond_wait调用成功，挂起当前线程之前，要先自动释放锁！！
            // 重点2：当线程被唤醒的时候，默认就在临界区内唤醒！要从pthread_cond_wait
            // 成功返回，需要当前线程，重新申请_mutex锁！！！
            // 重点3：如果我被唤醒，但是申请锁失败了？？我就会在锁上阻塞等待！！！
            _psleep_num++;
            std::cout << "生产者，进入休眠了: _psleep_num" <<  _psleep_num << std::endl;
            // 问题1: pthread_cond_wait是函数吗？有没有可能失败？pthread_cond_wait立即返回了
            // 问题2：pthread_cond_wait可能会因为，条件其实不满足，pthread_cond_wait 伪唤醒
            pthread_cond_wait(&_full_cond, &_mutex);
            _psleep_num--;
        }
        // 100%确定：队列有空间
        _q.push(in);

        // 临时方案
        // v2
        if(_csleep_num>0)
        {
            pthread_cond_signal(&_empty_cond);
            std::cout << "唤醒消费者..." << std::endl;
        }

        // pthread_cond_signal(&_empty_cond); // 可以
        pthread_mutex_unlock(&_mutex); // TODO
        // pthread_cond_signal(&_empty_cond); // 可以
    }
    T Pop()
    {
        // 消费者调用
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        {
            _csleep_num++;
            pthread_cond_wait(&_empty_cond, &_mutex);
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();

        if(_psleep_num > 0)
        {
            pthread_cond_signal(&_full_cond);
            std::cout << "唤醒消费者" << std::endl;
        }

        // pthread_cond_signal(&_full_cond);
        pthread_mutex_unlock(&_mutex);
        return data;
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full_cond);
        pthread_cond_destroy(&_empty_cond);
    }

private:
    std::queue<T> _q; // 临界资源！！！
    int _cap;         // 容量大小

    pthread_mutex_t _mutex;
    pthread_cond_t _full_cond;
    pthread_cond_t _empty_cond;

    int _csleep_num; // 消费者休眠的个数
    int _psleep_num; // 生产者休眠的个数
};

//Task.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <functional>

// 任务形式2
// 我们定义了一个任务类型，返回值void，参数为空
using task_t = std::function<void()>;

void Download()
{
    std::cout << "我是一个下载任务..." << std::endl;
    sleep(3); // 假设处理任务比较耗时
}

// 任务形式1
class Task
{
public:
    Task(){}
    Task(int x, int y):_x(x), _y(y)
    {
    }
    void Execute()
    {
        _result = _x + _y;
    }
    int X() { return _x; }
    int Y() { return _y; }
    int Result()
    {
        return _result;
    }
private:
    int _x;
    int _y;
    int _result;
};



//Main.cc
#include "BlockQueue.hpp"
#include "Task.hpp"
#include <iostream>
#include <pthread.h>
#include <unistd.h>

void *consumer(void *args)
{
    // BlockQueue<int> *bq = static_cast<BlockQueue<int>*>(args);
    // while (true)
    // {
		// sleep(1);
        // int data= bq->Pop();
        // std::cout << "消费了一个数据: " << data << std::endl;
    // }
	
	// BlockQueue<Task> *bq = static_cast<BlockQueue<Task>*>(args);
    // while (true)
    // {
		// sleep(1);
        // Task t= bq->Pop();
		// t.Execute();
        // std::cout << "消费了一个任务: " << t.X() << "+" <<t.Y() << "=" << t.Result() << std::endl;
    // }
	
	BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t> *>(args);
    while (true)
    {
        sleep(10);
        // 1. 消费任务
        task_t t = bq->Pop();

        // 2. 处理任务 -- 处理任务的时候，这个任务，已经被拿到线程的上下文中了,不属于队列了
        t();
    }

}

void *productor(void *args)
{
	// int data=1;
    // BlockQueue<int> *bq = static_cast<BlockQueue<int>*>(args);
    // while (true)
    // {
		////sleep(1);
		// std::cout << "生产了一个数据: " << data << std::endl;
        // bq->Equeue(data);
		// data++;
    // }
	
	// int x=1;
	// int y=1;
    // BlockQueue<Task> *bq = static_cast<BlockQueue<Task>*>(args);
    // while (true)
    // {
		////sleep(1);
		// std::cout << "生产了一个任务: " << x << "+" << y << "=?" << std::endl;
		// Task t(x,y);
        // bq->Equeue(t);
		// x++;
		// y++;
    // }
	
	BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t> *>(args);
    while (true)
    {
        // 1. 获得任务
        std::cout << "生产了一个任务: " << std::endl;

        // 2. 生产任务
        bq->Equeue(Download);
    }

}

int main()
{
    // 申请阻塞队列
    //BlockQueue<int> *bq = new BlockQueue<int>();
	
	// 扩展认识: 阻塞队列: 可以放任务吗？
	//BlockQueue<Task> *bq = new BlockQueue<Task>();
	 BlockQueue<task_t> *bq = new BlockQueue<task_t>();
	

    // 构建生产和消费者
    pthread_t c, p;

    pthread_create(&c, nullptr, consumer, bq);
    pthread_create(&p, nullptr, productor, bq);

    pthread_join(c, nullptr);
    pthread_join(p, nullptr);

    return 0;
}






//线程同步demo1
#define NUM 5
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER; // 定义锁, 为什么一定要有锁？？
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;   // 定义条件变量

// 等待是需要等，什么条件才会等呢？比如之前的票数为0，等待之前就要对资源的数量进行判定。
// 判定本身就是访问临界资源！所以判断一定是在临界区内部的。
// 判定结果，也一定在临界资源内部。所以，条件不满足要休眠，一定是在临界区内休眠的!
// 这个demo证明一件事情：条件变量，可以允许线程等待，可以允许一个线程唤醒在cond等待的其他线程，实现同步过程。
void *threadrun(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        pthread_mutex_lock(&glock);
        // 直接让对应的线程进行等待，等待往往是临界资源不满足导致我们等待的!
        pthread_cond_wait(&gcond, &glock); // glock在pthread_cond_wait之前，会被自动释放掉
        std::cout << name << " 计算: " << cnt << std::endl;
        cnt++;
        pthread_mutex_unlock(&glock);
    }
}

int main()
{
    std::vector<pthread_t> threads;
    for (int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        char *name = new char[64];
        snprintf(name, 64, "thread-%d", i);
        int n = pthread_create(&tid, nullptr, threadrun, name);
        if (n != 0)
            continue;
        threads.push_back(tid);
        sleep(1);
    }

    sleep(3);

    // 每隔1s唤醒一个线程
    while(true)
    {
        std::cout << "唤醒所有线程... " << std::endl;
        pthread_cond_broadcast(&gcond);
        
        // std::cout << "唤醒一个线程... " << std::endl;
        // pthread_cond_signal(&gcond);
        sleep(1);
    }

    for (auto &id : threads)
    {
        int m = pthread_join(id, nullptr);
        (void)m;
    }

    return 0;
}
